iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0

昨天介紹了繼承,它讓我們可以制定一個較為通用的父類別,透過子類別去繼承父類別並對父類別的變數與函式進行修改或是額外新增專屬子類別的變數與函式去描繪現實世界。但這樣的做法變相要求工程師必須要先將父類別中的所有函式與變數,另外對於繼承的行為上也只限於繼承一個父類別。為此,物件導向語言常提供介面 (interface) 的寫法去突破類別繼承的限制。

在Kotlin中,介面可以包含抽象函式(沒有具體實作的函式)以及具體函式(有具體實作)。類別可以實作(implement)介面,可以想像成繼承概念,但因為介面本身並不是一個類別,所以我們會用 “實作” 去描述將介面擴展成類別的過程。這裡我們整理一些介面之餘類別的優勢

  1. 一個類別可以實作多個介面,但只能繼承一個類別。
  2. 介面中可以同時存在抽象與具體函式,類別中只能存在具體函式。
  3. 介面用於訂定公開的街口辦不透露內容實作,類別在公開過程會一併公開公版實作。
  4. Kotlin 的介面可以與 Java 直接混用,增加擴展性。

定義:

在 Kotlin 中,我們使用 interface 搭配介面名稱去宣告一個介面,介面可以包含抽象函式,這些函式沒有具體實作。實作介面的類別必須為這些方法提供具體實作。例如:

interface MyInterface {
    fun doSomething() // 抽象方法
}

介面也可以包含抽象變數,這些屬性沒有初始值:

interface MyInterface {
    val property: Int // 抽象屬性
}

介面可以包含方法實作(具體函式)。這些函式在介面中實作,提供預設行為。實作介面的類別可以選擇覆寫這些方法或使用預設實作:

interface MyInterface {
    fun doSomething() {
        println("在介面中做些事情")
    }
}

實作介面 (implement interface):

類別可以使用:符號,後接介面名稱,實作一個或多個介面:

class MyClass : MyInterface {
    override fun doSomething() {
        println("在類別中做些事情")
    }
}

如果一個類別實作了具有相同名稱方法的多個介面,則必須有另外處理:

interface InterfaceA {
    fun commonMethod() {
        println("InterfaceA commonMethod()")
    }
}

interface InterfaceB {
    fun commonMethod() {
        println("InterfaceB commonMethod()")
    }
}

// 這會會出現編譯錯誤。
class MyClass : InterfaceA, InterfaceB {
		// do something
}

我們必須將其修改為直接告訴編譯器我們期望怎麼做:

class MyClass : InterfaceA, InterfaceB {
    override fun commonMethod() {
        println("Implemented commonMethod()")
    }
}

fun main() {
    val myClass = MyClass()
    myClass.commonMethod() // 輸出: Implemented commonMethod()
}

或也可以指定要從哪個介面去實作:

class MyClass : InterfaceA, InterfaceB {
    override fun commonMethod() {
        // 呼叫 InterfaceA 的 commonMethod()
        super<InterfaceA>.commonMethod()
    }
}

fun main() {
    val myClass = MyClass()
    myClass.commonMethod() // 輸出: InterfaceA commonMethod()
}

實作有屬性的介面:

相似於介面函式實作過程,如果介面有提供就必須實作。

class MyClass : MyInterface {
    override val property: Int = 42
}

介面繼承 (interface inheritance)

除了類別可以繼承類別、類別可以實作介面外,其實介面也是可以繼承介面的?讓我們看一個範例:

interface Named {
    val name: String
}

interface Person : Named {
    val firstName: String
    val lastName: String

    override val name: String get() = "$firstName $lastName"
}

class Employee(
    firstName: String,
    lastName: String,
    position: String
) : Person {
    override val firstName: String = firstName
    override val lastName: String = lastName
    val position: String = position
    
		fun greeting(): String {
	        return name
    }
}
fun main() {
    val member = Employee("John", "Wu", "Engineer")
    println(member.greeting())  // John Wu
}

上述範例先定義了一個介面 Named,然後由 Person 介面繼承 Named 介面,並透過本身有兩個變數 firstNamelastName組成 name。這個行為介面覆寫了 Named 介面的 name 變數,使得最後可以在 Employee 類別的物件中直接訪問複寫的 name 變數。

參考資料


上一篇
Day 19 Kotlin 繼承
下一篇
Day 21 Kotlin 抽象化類別
系列文
成為一名 Kotlin 後端攻城獅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言